#include "userlib.h"

static void set(char *line);
static void listsubtasks(void);
static void listvmas(void);
static void find(char *line);
static void deleteresult(char *line);
static void printresult(char *line);
static void intersectresult(char *line);

static void showcommands(void)
{
	printf("*** All Commands ***\n"
	 "f\n"
	 "    Find a value. Automatically save the result into a new result set.\n"
	 "    Format: f value\n"
	 "    Example: f 12000\n\n"
	 "set\n"
	 "    Write memory.\n"
	 "    Format: set pid addr value\n"
	 "    Format: set pid addr value len\n"
	 "rs\n"
	 "    Print the info of all result-sets.\n\n"
	 "r\n"
	 "    Print the info of a certain result-set\n"
	 "    Example: r1\n\n"
	 "^\n"
	 "    Intersect two result-sets. Automatically save the result into a new result set.\n"
	 "    Example: ^ r1 r2\n\n"
	 "del\n"
	 "    Delete a certain result-set\n"
	 "    Example: del r1\n\n"	 
	 "tasks\n"
	 "t\n"
	 "    List pid of the game and its child processes.\n\n"
	 "vma\n"
	 "    List virtual memory areas of the game and its child processes.\n\n"
	 "q\n"
	 "    Terminate the game and exit linalter.\n\n"	 
	 "h\n"
	 "?\n"
	 "    Show this help.\n\n"
	 );
}

int main(int argc, char *argv[])
{
	char line[100];
	char cmd[15];
	
	startsubprocess(argc, argv);
    puts("Linalter");
	puts("Alter Linux game's memory dynamically.");

	stoptask();
	int h = haswritablevma();
	continuetask();
	if(h==0){
		printf("No writable vma found, maybe the game is started as root. Please start me by 'sudo linalter'.\n");
	}

	while(1){
		printf("> "); fflush(stdout);
		if( fgets(line, sizeof(line)-1, stdin) ){
			cmd[0]=0;
			sscanf(line, "%s", cmd);
			
			if( !strcmp(cmd, "q") ) kill(0, SIGKILL);
			else if( !strcmp(cmd, "h") || !strcmp(cmd, "?") ) showcommands();
			else if( !strcmp(cmd, "tasks") || !strcmp(cmd, "t") ) listsubtasks();
			else if( !strcmp(cmd, "vma") ) listvmas();
			else if( !strcmp(cmd, "set") || !strcmp(cmd, "s") ) set(line);
			else if( !strcmp(cmd, "f") || !strcmp(cmd, "find") ) find(line);
			else if( !strcmp(cmd, "resultsets") || !strcmp(cmd, "rs") ) listresultsets();
			else if( strcmp(cmd, "del") == 0 ) deleteresult(line);
			else if( cmd[0]=='r' ) printresult(line);
			else if( strcmp(cmd, "^") == 0 ) intersectresult(line);
			else printf("Unknown command. Run '?' for help.\n");
		}else
			kill(0, SIGKILL);
	}
	
	return 0;
}

void intersectresult(char *line)
{
	int id1, id2;
	if( sscanf(line, "%*s r%d r%d", &id1, &id2) == 2){
		ResultSet *r1 = findresultset(id1), *r2 = findresultset(id2);
		if(!r1)
			fprintf(stderr, "ResultSet r%d does not exist.\n", id1);
		else if(!r2)
			fprintf(stderr, "ResultSet r%d does not exist.\n", id2);
		else{
			ResultSet *r3 = new ResultSet(r1, r2);
			r3->printinfo();
			results.push_back(r3);
		}
	}else
		fprintf(stderr, "Correct format is \"^ r%%d r%%d\"\n");
}

void printresult(char *line)
{
	int id;
	if( sscanf(line, "r%d", &id) == 1){
		ResultSet *r = findresultset(id);
		if(r){
			r->printinfo();
			r->printaddrs();
		}else
			fprintf(stderr, "ResultSet r%d does not exist.\n", id);
	}else
		fprintf(stderr, "Correct format is \"r%%d\"\n");
}

void deleteresult(char *line)
{
	int id;
	if( sscanf(line, "%*s r%d", &id) == 1){
		if(deleteresultset(id))
			fprintf(stderr, "ResultSet r%d does not exist.\n", id);
	}else
		fprintf(stderr, "Correct format is \"del r%%d\"\n");	
}

void find(char *line)
{
	unsigned long long value;
	if( sscanf(line, "%*s %llu", &value) == 1){
		searchtaskmem(value);
	}else
		fprintf(stderr, "Correct format is \"f %%llu(value)\"\n");
}

void listvmas(void)
{
	stoptask();
	getwritablevma();
	printwritablevma();
	continuetask();
}

void listsubtasks(void)
{
	vector<long> tasks;
	unsigned i;
	
	gettracedtasks(&tasks);
	for(i=0; i<tasks.size(); i++)
		printf("%ld\n", tasks[i]);
}

void set(char *line)
{
	long pid;
	unsigned long addr;
	long long unsigned value;
	int len;
	int ret;
	if( (ret = sscanf(line, "%*s %ld %lx %llu %d", &pid, &addr, &value, &len))==4){
		settaskmem(pid, addr, value, len);
	}else if(ret == 3){
		settaskmem(pid, addr, value, minlen(value));
	}else
		puts("Correct format is \"set %%ld %%lx %%llu %%d\" set pid addr value [len]");
}
